package com.nulldev.util.concurrency.threadIt.v3.extensions;

import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.concurrency.threadIt.v4.asyncTasks.AsyncTaskCallback;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ft.FutureTask;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.internal.backport.concurrency9.Lists;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.manage.MarkedForRemoval;

@SuppressWarnings("rawtypes")
@MarkedForRemoval(reason = "threadIt! v3.x is deprecated and has been replaced with v4.0")
@Deprecated
public abstract class Task<T> implements _Task, Executable<T> {

	private static final Logger log = LoggerFactory.getLogger();

	static {
		log.warn("[threadIt!] V3 has been deprecated and marked for removal, please upgrade to v4 soon.");
		new Exception().printStackTrace();
	}

	private static final long DEFAULT_WAITING_INTERVAL = Arguments.hasArgument("--Task.useLongerWaitInterval") ? 65L : 35L;
	public volatile long waitingInterval = DEFAULT_WAITING_INTERVAL;

//	private static Void __get_void() {
//		if (JVM.version() < 1.9) {
//			return (Void) LegacyReflectionUtils.createInstance("java.lang.Void");
//		}else {
//			return null;
//		}
//	}

	private final BlockingQueue<AsyncTaskCallback<T>> callback;

	public static final Void VOID = null; // __get_void();

	private volatile Thread t;
	private volatile T result = null;
	private volatile TaskStatus status = TaskStatus.QUEUED;

	public Task() {
		this.callback = new LinkedBlockingQueue<AsyncTaskCallback<T>>();
	}

	public Task(final BlockingQueue<AsyncTaskCallback<T>> callback) {
		this.callback = callback;
	}

	public abstract T run();

	@Override
	public void run(final Thread currentThread) {
		this.status = TaskStatus.RUNNING;
		try {
			this.t = currentThread;
			this.result = this.run();
			this.status = TaskStatus.COMPLETED;
			this.t = null;
			if (!this.callback.isEmpty()) {
				AsyncTaskCallback<T> o;
				while ((o = this.callback.poll()) != null) {
					try {
						o.onCompletion(Task.this.result);
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
			}
		} catch (Throwable t) {
			this.result = null;
			this.status = TaskStatus.FAILED;
			this.t = null;
			if (!this.callback.isEmpty()) {
				AsyncTaskCallback<T> o;
				while ((o = this.callback.poll()) != null) {
					try {
						o.onError(t);
					} catch (Exception ec) {
						throw new RuntimeException(ec);
					}
				}
			}
			throw new RuntimeException(t);
		}
	}

	@Override
	public T _run() {
		this.status = TaskStatus.RUNNING;
		try {
			this.t = Thread.currentThread();
			this.result = this.run();
			this.status = TaskStatus.COMPLETED;
			this.t = null;
			if (!this.callback.isEmpty()) {
				AsyncTaskCallback<T> o;
				while ((o = this.callback.poll()) != null) {
					try {
						o.onCompletion(Task.this.result);
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
			}
		} catch (Throwable t) {
			this.result = null;
			this.status = TaskStatus.FAILED;
			this.t = null;
			if (!this.callback.isEmpty()) {
				AsyncTaskCallback<T> o;
				while ((o = this.callback.poll()) != null) {
					try {
						o.onError(t);
					} catch (Exception ec) {
						throw new RuntimeException(ec);
					}
				}
			}
			throw new RuntimeException(t);
		}
		return this.result;
	}

	/**
	 * Executes the task. <br>
	 * WARNING: This shouldn't be touched, this will freeze the calling thread.
	 */
	public void execute() {
		this.result = this._run();
	}

	@Override
	public List<AsyncTaskCallback<?>> callbacks() {
		return Lists.of(this.callback.toArray(new AsyncTaskCallback[this.callback.size()]));
	}

	private final static boolean ENABLE_NEW_WAIT_LOGIC = true;

	/**
	 * Freezes the current thread until either the following occur: <br>
	 * - The task completes and returns the result. <br>
	 * - The task fails, is killed and <code>NULL</code> is returned.
	 */
	@Override
	public T waitTillCompletion() {
		if (this.status == TaskStatus.COMPLETED) {
			return this.result;
		}
		final int limit = 8;
		int waitingInt = limit;
		final int s = (int) (this.waitingInterval / limit);
		while (true) {
			if (this.status == TaskStatus.FAILED || this.status == TaskStatus.KILLED) {
				return null;
			} else if (this.status == TaskStatus.COMPLETED) {
				break;
			} else {
				try {
					if (ENABLE_NEW_WAIT_LOGIC) {
						if (waitingInt <= 0) {
							waitingInt = limit + 1;
						}
						Thread.sleep((long) Math.ceil(s / waitingInt));
						waitingInt -= 1;
					} else {
						Thread.sleep(this.waitingInterval);
					}
				} catch (InterruptedException e) {
				}
			}
		}
		return this.result;
	}

	@Override
	public TaskStatus status() {
		return this.status;
	}

	@Override
	public boolean completed() {
		return this.status == TaskStatus.COMPLETED || this.status == TaskStatus.FAILED || this.status == TaskStatus.KILLED
				|| this.status == TaskStatus.CANCELLED || this.status == TaskStatus.SUBMISSION_FAILED;
	}

	@Override
	public void signalQueued() {
	}

	@Override
	public T result() {
		return this.result;
	}

	/**
	 * Attempts to kill the executing thread.
	 */
	public void kill() {
		this.status = TaskStatus.KILLED;
		if (this.t != null) {
			this.t.interrupt();
		} /*
			 * else { Thread.currentThread().interrupt(); }
			 */
	}

	public boolean addCallbackTask(final AsyncTaskCallback<T> task) {
		if (this.completed()) {
			task.onCompletion(result);
			return false;
		} else {
			try {
				return this.callback.offer(task, 300, TimeUnit.MILLISECONDS);
			} catch (Exception e) {
				return false;
			}
		}
	}

	@Deprecated
	public FutureTask<T> toV4Task() {
		return new FutureTask<T>(this.callback) {

			@Override
			public T execute() {
				return Task.this.run();
			}

			@Override
			public T run_st() {
				return Task.this.run_st();
			}
		};
	}

	@Override
	public T waitFor(final long amount, final TimeUnit unit) {
		if (amount < 0 || unit == null)
			throw new IllegalArgumentException("amount < 0!");
		if (this.completed())
			return this.result;
		final int limit = 8;
		int waitingInt = limit;
		final int s = (int) (this.waitingInterval / limit);
		final long start = System.currentTimeMillis();
		while ((System.currentTimeMillis() - start) < unit.toMillis(amount) && !this.completed()) {
//			JVM.println("waitFor(" + amount + ", " + unit + ")");
			try {
				if (ENABLE_NEW_WAIT_LOGIC) {
					if (waitingInt <= 0) {
						waitingInt = limit + 1;
					}
					Thread.sleep((long) Math.ceil(s / waitingInt));
					waitingInt -= 1;
				} else {
					Thread.sleep(this.waitingInterval);
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		return this.result;
	}

	@Override
	public T run_st() {
		return this._run();
	}

	@Override
	public String toString() {
		return "Task[hash=" + MathUtil.toHex(super.hashCode()) + ",state=" + this.status + ",result=" + this.result + "]";
	}
}
